/*****************************************************************************
*                     Organizacion de Datos - 75.06                          *
*                         Catedra Lic. Servetto                              *
*            Facultad de Ingenieria - Universidad de Buenos Aires            *
******************************************************************************/

#include "ParseadorComandos.h"
#include "AdministradorDeOperaciones.h"
#include "AdministradorMetadata.h"

#include <list>
#include <string>
#include <fstream>

namespace CapaConsulta 
{

	Operacion* ParseadorComandos::ParsearInsercion(list<string>* operacion)	{
		
		Insercion* operacionInsercion = new Insercion();
		
		/***************************************/
		/* INSERT                              */
		/***************************************/
		
		string insertSupuesto = operacion->front();
		if (insertSupuesto != "INSERT")
		{
			cerr << "La insercion en una clase espera el comando INSERT, se encontro " << insertSupuesto << "." << endl;
			throw new exception();
		}
		ParseadorComandos::SacarConComprobacion(operacion, "INSERT");
		
		/***************************************/
		/* INTO                                */
		/***************************************/
		
		string intoSupuesto = operacion->front();
		if (intoSupuesto != "INTO")
		{
			cerr << "La insercion en una clase espera el comando INTO despues de INSERT, se encontro " << intoSupuesto << "." << endl;
			throw new exception();
		}
		ParseadorComandos::SacarConComprobacion(operacion, "INSERT");
		
		/***************************************/
		/* Clase                               */
		/***************************************/
		
		string claseSupuesto = operacion->front();
	
		AdministradorMetadata* administrador = new AdministradorMetadata(claseSupuesto);
		
		if (!administrador->ExisteClase())
		{
			cerr << "No se puede encontrar la clase '" << claseSupuesto << "' en el repositorio." << endl;
			throw new exception();
		}
		ParseadorComandos::SacarConComprobacion(operacion, "INSERT");
		
		operacionInsercion->ClaseOperacion = administrador->ClaseMetadata;
		
		/***************************************/
		/* (                                   */
		/***************************************/
		
		string nombreParentesisComienzoSupuesto = operacion->front();
		if (nombreParentesisComienzoSupuesto != "(")
		{
			cerr << "La insercion en una clase espera que los nombres de los atributos esten entre parentesis (). Se encontro " << nombreParentesisComienzoSupuesto << " en ves de (." << endl;
			throw new exception();
		}
		ParseadorComandos::SacarConComprobacion(operacion, "INSERT");
		
		/***************************************/
		/* Atributo , Atributo )               */
		/***************************************/
		
		string nombreAtributoSupuesto = operacion->front();
		ParseadorComandos::SacarConComprobacion(operacion, "INSERT");
		int contadorAtributo = 1;
		
		while(nombreAtributoSupuesto != ")")
		{	
			string atributoACorroborar;
			atributoACorroborar = nombreAtributoSupuesto;
			
			ClaveValor claveSinValor;
			claveSinValor.Clave = atributoACorroborar;
			operacionInsercion->Valores->push_back(claveSinValor);
			
			if (!administrador->EsAtributoDeLaClase(atributoACorroborar))
			{
				cerr << "El atributo  " << atributoACorroborar << " no pertenece a la clase" << claseSupuesto << "." << endl;
				throw new exception();
			}
			
			string comaOParentesisSupuesto = operacion->front();
			
			if (comaOParentesisSupuesto != "," && comaOParentesisSupuesto != ")")
			{
				cerr << "La insercion en una clase espera los nombres de los atributos separados por comas y entre parentesis, en lugar de una , o un ) se encontro " << comaOParentesisSupuesto << "." << endl;
				throw new exception();
			}
			
			if (comaOParentesisSupuesto == ",")
			{
				ParseadorComandos::SacarConComprobacion(operacion, "INSERT");
				nombreAtributoSupuesto = operacion->front();
				ParseadorComandos::SacarConComprobacion(operacion, "INSERT");
			}
			else if (comaOParentesisSupuesto == ")")
			{
				nombreAtributoSupuesto = comaOParentesisSupuesto;
				ParseadorComandos::SacarConComprobacion(operacion, "INSERT");
			}
			
			contadorAtributo++;
		}
		contadorAtributo--;
		
		/***************************************/
		/* VALUES                              */
		/***************************************/
		
		string valuesSupuesto = operacion->front();
		if (valuesSupuesto != "VALUES")
		{
			cerr << "La insercion en una clase espera el comando VALUES entre los atributos y sus valores, se encontro " << valuesSupuesto << "." << endl;
			throw new exception();
		}
		ParseadorComandos::SacarConComprobacion(operacion, "INSERT");
		
		/***************************************/
		/* (                                   */
		/***************************************/
		
		nombreParentesisComienzoSupuesto = operacion->front();
		if (nombreParentesisComienzoSupuesto != "(")
		{
			cerr << "La insercion en una clase espera que los valores de los atributos esten entre parentesis (). Se encontro " << nombreParentesisComienzoSupuesto << " en ves de (." << endl;
			throw new exception();
		}
		ParseadorComandos::SacarConComprobacion(operacion, "INSERT");
		
		/***************************************/
		/* 'Valor' , 'Valor' )                */
		/***************************************/
		
		string valorAtributoSupuesto = operacion->front();
		ParseadorComandos::SacarConComprobacion(operacion, "INSERT");
		int contadorAtributoValor = 1;
		list<ClaveValor>::iterator iteradorDeValores = operacionInsercion->Valores->begin();
		while(valorAtributoSupuesto != ")")
		{
			if (contadorAtributoValor > contadorAtributo)
			{
				cerr << "Hay mas valores que atributos declarados, se habian declarado " << contadorAtributo << " atributos." << endl;
				throw new exception();
			}
			
			string valorAtributoLimpio;
			if (valorAtributoSupuesto[0] == '\'')
			{
				if (!ParseadorComandos::ValidarDatosEntreComillasSimples(valorAtributoSupuesto))
				{
					cerr << "Los datos deben estar entre comillas simples ''. Se encontro " << valorAtributoSupuesto << "." << endl;
					throw new exception();
				}
				valorAtributoLimpio = ParseadorComandos::EliminarComillasSimplesDeDatos(valorAtributoSupuesto);
			}
			else
			{
				valorAtributoLimpio = valorAtributoSupuesto;
			}
			
			if (!administrador->ElValorDelAtributoEsCorrecto((*iteradorDeValores).Clave, valorAtributoSupuesto))
			{
				cerr << "No se puede parsear correctamente el tipo del atributo " << (*iteradorDeValores).Clave << ". El valor '" << valorAtributoSupuesto << "' no es correcto." << endl;
				throw new exception();
			}
			
			(*iteradorDeValores).Valor = valorAtributoLimpio;
			iteradorDeValores++;
			
			string comaOParentesisSupuesto = operacion->front();
			
			if (comaOParentesisSupuesto != "," && comaOParentesisSupuesto != ")")
			{
				cerr << "La insercion en una clase espera los nombres de los atributos separados por comas y entre parentesis, en lugar de una , o un ) se encontro " << comaOParentesisSupuesto << "." << endl;
				throw new exception();
			}
			
			if (comaOParentesisSupuesto == ",")
			{
				ParseadorComandos::SacarConComprobacion(operacion, "INSERT");
				valorAtributoSupuesto = operacion->front();
				ParseadorComandos::SacarConComprobacion(operacion, "INSERT");
			}
			else if (comaOParentesisSupuesto == ")")
			{
				valorAtributoSupuesto = comaOParentesisSupuesto;
				operacion->pop_front();
			}
			
			contadorAtributoValor++;
		}
		contadorAtributoValor--;
		
		if (contadorAtributoValor != contadorAtributo)
		{
			cerr << "Se especificaron " << contadorAtributo << " atributo(s) pero solo " << contadorAtributoValor << " valor(es)." << endl;
			throw new exception();
		}
		
		if (administrador->ClaseMetadata->Miembros->size() != contadorAtributo)
		{
			cerr << "La clase " << administrador->ClaseMetadata->NombreClase->c_str() << " tiene " << administrador->ClaseMetadata->Miembros->size() << " atributos, pero en el INSERT solo se insertaron " << contadorAtributo << "." << endl;
			throw new exception();
		}
		
		/***************************************/
		/* Validaciones                        */
		/***************************************/
		
		if (iteradorDeValores != operacionInsercion->Valores->end())
		{
			cerr << "Hay menos valores que atributos declarados, se habian declarado " << contadorAtributo << " atributos y se encontraron " << contadorAtributoValor << " valores." << endl;
			throw new exception();
		}
		
		if (!operacion->empty())
		{
			cerr << "La insercion se parseo correctamente pero quedaron " << operacion->size() << " cadena(s) no conocidas por interpretar para este comando." << endl;
			throw new exception();
		}
		
		return operacionInsercion;
	};
	
}
